package com.g001.service.match3.IA;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.newdawn.slick.SlickException;

import com.g001.enums.ProxBlockType;
import com.g001.model.GCharacter;
import com.g001.model.GameBlock;
import com.g001.model.IBlock;
import com.g001.util.BlockUtils;

public class BrainService {
	
	Random random = new Random(); // for the level of bad choice it can do.
	/**
	 * return the *better/approriate* action for the character1 regarding the situation(life energy etc) of character2.
	 * for example :
	 *  - character 1 is running out of life, but character 2 also and he can be beaten by one attack.
	 *  instead to increase his life, the algo will chose the attack action to kill character2.
	 *  
	 *  if it returns NULL it means the matrix has to be refresh because no combination is possible
	 * @param character
	 * @param blocks
	 * @return
	 * @throws SlickException  
	 */
	public IBlock[] findBestChoiceForCharacter(List<IBlock> blocks, GCharacter character1, GCharacter character2) throws SlickException {
		
		// first we have to see if we found a solution
		List<IBlock[]> potentialMoves = new ArrayList<IBlock[]>();

		// we work with a copy of data
		List<IBlock> copy = BlockUtils.copyWithoutRendition(blocks);
		List<Combination> combinationsAlreadyChecked = new ArrayList<Combination>(copy.size()*6);
		IBlock px = null;
		for(IBlock block : copy) {
			// we check only if it is a GameBlock and movable
			if(block instanceof GameBlock && block.getBlockType().isMovable()) {
				
			for(ProxBlockType pbt : block.getProxiBlocks().keySet()){
				px = block.getProxyBlock(pbt);
				if(px!=null && px instanceof GameBlock && px.getBlockType().isMovable()) {
					Combination c = new Combination(block,px);
					if(!c.combinationInList(combinationsAlreadyChecked) && px.getBlockType().getId()!=block.getBlockType().getId()) {
						combinationsAlreadyChecked.add(c);
						// we will not check proxy block
						switchBlocks(block, px);
						if(findMatch(block, px)){
							// we found a combi
							IBlock[] tab = new IBlock[2];
							tab[0] = block;
							tab[1] = px;
							potentialMoves.add(tab);
						}
						// put back to test the others ...
						switchBlocks(block, px);
					}
				}
			}
			}
		}
		copy.clear();
		combinationsAlreadyChecked.clear();
		return (potentialMoves.isEmpty()? null: potentialMoves.get(0));
		
	}
	
	
	public boolean findMatch(IBlock block1, IBlock block2) throws SlickException {
		if(block1!=null && block2!=null && block1.getBlockType().getId() == block2.getBlockType().getId()) {
			// not autorize to switch same color
			return false;
		}
		boolean v1 = match(block1); // both have to be called !
		boolean v2 = match(block2); // do not put in the return (lazy test)
		block1.setRemove(v1);
		block2.setRemove(v2);
		return v1 || v2;
	}
	
	public boolean match(IBlock block) throws SlickException {
		boolean res = false;
		if (block!=null) {			
			// process blocks N -> S
			boolean res1 = matchSubTask(block, ProxBlockType.S);
			// NW -> SE
			boolean res3 = matchSubTask(block, ProxBlockType.NW);
			// NE -> SW
			boolean res2 = matchSubTask(block, ProxBlockType.NE);
			// final result
			res = res1 || res2 || res3;
		} 
		return res;
	}		
	
	private boolean matchSubTask(IBlock block, ProxBlockType proxyType) throws SlickException {
		boolean res1 = false;
		if(block != null) {
		IBlock b = block.findIdenticalBlock(proxyType, block.getProxiBlocks().get(proxyType));
			res1 = b.processBlock2remove(ProxBlockType.getOppositeBlockType(proxyType));
		}
		return res1;
	}
	
	public void switchBlocks(IBlock b1, IBlock b2) throws SlickException {
		Map<ProxBlockType,IBlock> proxblocksB1Updated = b1.getProxiBlocks();
		Map<ProxBlockType,IBlock> proxblocksB2Updated = b2.getProxiBlocks();
		IBlock b;
		// update links of outgoing way
		// the proxyblock of B1
		for(ProxBlockType key : b1.getProxiBlocks().keySet()) {
			b = b1.getProxiBlocks().get(key);
			if(b == b2) {
				proxblocksB1Updated.put(key, b1);
			} else {
				proxblocksB1Updated.put(key, b);
			}
		}
		
		// the proxyblock of B2
		for(ProxBlockType key : b2.getProxiBlocks().keySet()) {
			b = b2.getProxiBlocks().get(key);
			if(b == b1) {
				proxblocksB2Updated.put(key, b2);
			} else {
				proxblocksB2Updated.put(key, b);
			}
		}
		b1.setProxiBlocks(proxblocksB2Updated);
		b2.setProxiBlocks(proxblocksB1Updated);
		
		IBlock k;
		for(ProxBlockType key : b1.getProxiBlocks().keySet()) {
			k = b1.getProxyBlock(key);
			b1.setProxyBlock(key, k);
		}
		for(ProxBlockType key : b2.getProxiBlocks().keySet()) {
			k = b2.getProxyBlock(key);
			b2.setProxyBlock(key, k);
		}
		
		
		
	}
	
	
	
	private class Combination {
		IBlock b1;
		IBlock b2;
		
		public Combination(IBlock b1, IBlock b2){
			this.b1 = b1;
			this.b2 = b2;
		}
		
		public boolean combinationInList(List<Combination> combinations){
			boolean result = false;
			for(Combination c : combinations) {
				if((c.b1 == b1 && c.b2 == b2) || (c.b2 == b1 && c.b1 == b2)) {
					result = true;
					break;
				}
			}
			return result;
		}
		
	} 
	

}
